<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2024/3/20
 * Time: 11:46
 */

namespace app\queue\planpart;

use app\common\lib\Common;
use app\common\lib\MoyinTts;
use app\common\lib\RedisQueue;
use app\common\lib\TencentTts;
use app\exception\IndexException;
use app\model\image\ImageModelModel;
use app\model\novel\NovelChapterModel;
use app\model\novel\NovelChapterPartModel;
use app\model\plan\PromotionPlanModel;
use app\model\queue\AiImageRequestQueueModel;
use app\model\redis\RedisQueueModel;
use app\model\tencent\TimbreModel;
use app\model\novel\NovelPlanRoleModel;
use support\Cache;
use Webman\RedisQueue\Consumer;
use app\common\lib\TencentCos;

class AsyncPlanPart  implements Consumer
{
    // 要消费的队列名
    public $queue = 'asyncPlanPart';

    // 连接名，对应 plugin/webman/redis-queue/redis.php 里的连接`
    public $connection = 'default';

    protected $resultData = [];

    // 消费
    public function consume($data)
    {
        Common::initQueueConfig();

        $queue = RedisQueueModel::getInstance()->find($data['queueid']);
        $queue->exec_time = date('Y-m-d H:i:s');
        $queue->save();

        try{
            // 无需反序列化
            //var_export($data);
            //var_export('redis_queue_asyncUploadVideo');
            $result = $this->handle($data);

            RedisQueue::getInstance()->saveQueue($this->queue,$data,$result);
        }catch (\Throwable $e){
            $cacheKey = 'async_plan_part_'.$data['requestid'];
            Cache::set($cacheKey,['progress'=>-1,'error'=>$e->getMessage()],7200);

            RedisQueue::getInstance()->saveQueue($this->queue,$data,['error'=>$e->getMessage()]);
            Common::queueExecptionReport($queue,$data,$e);
        }
    }

    protected function handle($data)
    {
        $timeLogs = [];
        $cacheKey = 'async_plan_part_'.$data['requestid'];
        Cache::set($cacheKey,['progress'=>'0'],7200);

        $batch = '';
        if(empty($data['type']) || $data['type']=='all'){
            //todo 1 生成创作内容片段
            $startTime = time();
            $batch = $this->generatedPart($data);
            Cache::set($cacheKey,['progress'=>'20'],7200);
            $timeLogs['generatedPart'][] = round(time()-$startTime);

            //todo 2 生成创作内容片段-字幕
            $startTime = time();
            $this->generatedSubtitle($data,$batch);
            Cache::set($cacheKey,['progress'=>'40'],7200);
            $timeLogs['generatedSubtitle'][] = round(time()-$startTime);

            //todo 3 生成创作内容片段-配音
            $startTime = time();
            $this->generatedTencentTimbre($data,$batch);
            Cache::set($cacheKey,['progress'=>'60'],7200);
            $timeLogs['generatedTencentTimbre'][] = round(time()-$startTime);

            //todo 4 生成创作内容片段-英文描述词
            $startTime = time();
            $this->generatedDesc($data,$batch);
            Cache::set($cacheKey,['progress'=>'80'],7200);
            $timeLogs['generatedDesc'][] = round(time()-$startTime);

            //todo 5 生成创作内容片段-ai漫画
            $startTime = time();
            $this->generatedAIImage($data,$batch);
            Cache::set($cacheKey,['progress'=>'100'],7200);
            $timeLogs['generatedAIImage'][] = round(time()-$startTime);

        }elseif($data['type']=='part'){
            //todo 1 生成创作内容片段
            $startTime = time();
            $batch = $this->generatedPart($data);
            Cache::set($cacheKey,['progress'=>'30'],7200);
            $timeLogs['generatedPart'][] = round(time()-$startTime);

            //todo 2 生成创作内容片段-字幕
            $startTime = time();
            $this->generatedSubtitle($data,$batch);
            Cache::set($cacheKey,['progress'=>'60'],7200);
            $timeLogs['generatedSubtitle'][] = round(time()-$startTime);

            //todo 3 生成创作内容片段-配音
            $startTime = time();
            $this->generatedTencentTimbre($data,$batch);
            Cache::set($cacheKey,['progress'=>'100'],7200);
            $timeLogs['generatedTencentTimbre'][] = round(time()-$startTime);
        }elseif($data['type']=='image'){
            $first = NovelChapterPartModel::getInstance()->getWhere(['planid'=>$data['planid'],'isdel'=>0])->orderBy('batch','desc')->orderBy('number','asc')->first();
            $batch = $first['batch'];
            Cache::set($cacheKey,['progress'=>'50'],7200);

            //todo 5 生成创作内容片段-ai漫画
            $startTime = time();
            $this->generatedAIImage($data,$batch);
            Cache::set($cacheKey,['progress'=>'100'],7200);
            $timeLogs['generatedAIImage'][] = round(time()-$startTime);
        }

        $partCount = NovelChapterPartModel::getInstance()->getWhere(['planid'=>$data['planid'],'batch'=>$batch,'isdel'=>0])->count();
        $contentText = '';
        foreach($timeLogs as $key=>$logs){
            $contentText .= $key.':'.implode(', ',$logs)."  ";
        }
        $contentLog = date('Y-m-d H:i:s') . "   ".$partCount."  ". $contentText . "\n" . "\n";
        file_put_contents(runtime_path('asyncPlanPart-time.log'), $contentLog, 8);

        return ['success'];
    }

    protected function generatedPart($data)
    {
        $chapterid = PromotionPlanModel::getInstance()->getWhere(['id'=>$data['planid']])->value('chapterid');
        $content = NovelChapterModel::getInstance()->getWhere(['id'=>$chapterid])->value('content');
        //$analyzeContent = NovelChapterModel::getInstance()->getWhere(['id'=>$chapterid])->value('analyze_content');
        $promptContent = NovelChapterModel::getInstance()->getWhere(['id'=>$chapterid])->value('prompt_content');
        if(empty($content)){
            throw new IndexException(4020,'分段内容不能为空');
        }
        if(empty($promptContent)){
            throw new IndexException(4020,'英文描述词不能为空');
        }
        $replaceStr =  ['`','~','@','#','$','^','&','*','(',')','=','|','{','}','\\','[',']','<','>','·','+','-','_',
                        '《','》','/','￥','……','（','）','――','【','】','‘','”', '“','"','\'','’',' ','「','」'];
        $contents = str_replace($replaceStr,"",$content);
        $contents = array_values(array_filter(explode("\n",preg_replace('/@[=]+@/',"\n",$contents))));
        foreach($contents as $k=>$content) {
            $content = preg_replace('/^\d+[\.]*/', '', $content);
            if (empty($content)) {
               unset($contents[$k]);
            }
        }
        $contents = array_values($contents);

        $promptContents = explode("\n",$promptContent);
        foreach($promptContents as $k=>$promptContent) {
            $promptContent = preg_replace('/^\d+[\.]+/', '', $promptContent);
            //$promptContent = preg_replace('/boy[ ]*\)/','boy:2)',$promptContent);

            //黑名单词过滤
            $aiCommandBlackListWorks = array_filter(explode('|@@|',config('param.ai_command_blacklist_works','')));
            $pregs = [];
            foreach($aiCommandBlackListWorks as $val){
                $pregs[] = ' '.ucfirst(trim($val)).' ';
                $pregs[] = ' '.lcfirst(trim($val)).' ';
            }
            $promptContent = str_replace($pregs,' ',$promptContent);
            if (empty($promptContent)) {
                unset($promptContents[$k]);
            }
        }
        $promptContents = array_values($promptContents);

        //$analyzeContents = explode("\n",$analyzeContent);
        if(count($promptContents)!=count($contents)){
            throw new IndexException(4020,'章节分段数和英文描述词分段数不一致');
        }

        //插入内容片段
        $novelSn = PromotionPlanModel::getInstance()->getWhere(['id'=>$data['planid']])->value('novel_sn');
        $chapterid = PromotionPlanModel::getInstance()->getWhere(['id'=>$data['planid']])->value('chapterid');
        $batch = date('ymdHis').mt_rand(10,99);

        //角色
        $roleData = NovelPlanRoleModel::getInstance()->getWhere(['planid'=>$data['planid'],'isdel'=>0])->get()->toArray();
        $insert = [];
        foreach($contents as $k=>$content){
            if(empty($content)){
                continue;
            }
            $content = preg_replace('/^\d+[\.]+/','',$content);
            $promptContent = preg_replace('/^\d+[\.]+/','',$promptContents[$k]);
            //$promptContent = preg_replace('/boy[ ]*\)/','boy:2)',$promptContent);

            $roleids = [];
            foreach ($roleData as $role){
                if(mb_strpos($content,$role['name'])!==false){
                    $roleids[] = (string)$role['id'];
                }
            }
            $insert[] = [
                'planid'=>$data['planid'],
                'novel_sn'=>$novelSn,
                'chapterid'=>$chapterid,
                'content'=>$content,
                'number'=>$k+1,
                'batch'=>$batch,
                'analyze_content'=>$analyzeContents[$k]??'',
                'prompt'=>$promptContent,
                'roleids'=>json_encode($roleids,256),
            ];
        }

        $insert && NovelChapterPartModel::getInstance()->getWhere(['planid'=>$data['planid']])->update(['isdel'=>time()]);
        $insert && NovelChapterPartModel::getInstance()->insert($insert);
        if(isset($data['from']) && $data['from']=='cron'){
            $insert && PromotionPlanModel::getInstance()->getWhere(['id'=>$data['planid']])->update(['part_nums'=>count($insert),'status'=>0]);
        }else{
            $insert && PromotionPlanModel::getInstance()->getWhere(['id'=>$data['planid']])->update(['part_nums'=>count($insert),'status'=>1]);
        }

        return $batch;
    }

    protected function generatedSubtitle($data,$batch)
    {
        $parts = NovelChapterPartModel::getInstance()->getWhere(['planid'=>$data['planid'],'batch'=>$batch,'isdel'=>0])
                                                     ->orderBy('number','asc')->get();
        foreach ($parts as $val){
            $replaceStr =  ['`','~','!','@','#','$','^','&','*','(',')','=','|','{','}','\\',':',';',',','[',']','.','+','-','_',
                            '<','>','《','》','/','?','！','￥','……','（','）','――','|','【','】',
                            '‘','；','：','”','“','。','，','、','？',', ','\'','"','’',' '];
            $content = str_replace($replaceStr, "\n",$val['content']);
            $contents = array_values(array_filter(explode("\n",$content)));

            $val->subtitle = json_encode($contents,256);
            $val->save();
        }

        return true;
    }

    protected function generatedTencentTimbre($data,$batch)
    {
        //队列名
        $queue = 'generateTimbre';

        // 数据，可以直接传数组，无需序列化
        $data = ['requestid'=>Common::getRequestId(), 'planid'=>$data['planid'], 'batch'=>$batch, 'source'=>'plan_part'];

        // 投递消息
        RedisQueue::getInstance()->sendQueue($queue, $data);

        return true;
    }

    protected function generatedDesc($data,$batch)
    {
        $parts = NovelChapterPartModel::getInstance()->getWhere(['planid' => $data['planid'], 'batch' => $batch, 'isdel' => 0])
            ->orderBy('number', 'asc')->get();
        foreach ($parts as $val) {
            if(!empty($val['prompt'])){
                continue;
            }

            //todo 0 分析该段描述
//            if (empty($val['analyze_content'])) {
//                try {
//                    $contents = Common::generatedDescWork($val['content'], $val['analyze_content_ai_command'] ?? '');
//                } catch (IndexException $e) {
//                    //失败再试一次
//                    if (strpos(strtolower($e->getMessage()), 'curl') !== false) {
//                        $contents = Common::generatedDescWork($val['content'], $val['analyze_content_ai_command'] ?? '');
//                    } else {
//                        throw $e;
//                    }
//                }
//                $val->analyze_content = $contents;
//            } else {
//                $contents = $val['analyze_content'];
//            }

            //todo 1 获取描述词
            try {
                $prompt = Common::generatedImagePrompt($val['content'], $val['prompt_ai_command'] ?? '');
            } catch (IndexException $e) {
                if (strpos(strtolower($e->getMessage()), 'curl') !== false) {
                    $prompt = Common::generatedImagePrompt($val['content'], $val['prompt_ai_command'] ?? '');
                } else {
                    throw $e;
                }
            }
            $val->prompt = $prompt;

            $val->save();
        }

        return true;
    }

    protected function generatedAIImage($data,$batch)
    {
        $plan = PromotionPlanModel::getInstance()->getWhere(['id'=>$data['planid']])->first();
//        $modelid = $plan['modelid']??'0';
//        $model = ImageModelModel::getInstance()->find($modelid);
//        $modelVal = $model ? $model['model'] : 'OIA Illustrator_0.10.safetensors [3328078dcc]';

        if($plan==false){
            throw new IndexException(4020,'方案不存在');
        }
        $aiImageCommands = json_decode($plan['ai_image_command'], true);
        if($aiImageCommands==false){
            throw new IndexException(4020,'生图指令JSON格式错误');
        }
        $modelVal = $aiImageCommands['override_settings']['sd_model_checkpoint']??'';
        if($modelVal==false){
            throw new IndexException(4020,'模型不能为空');
        }

        $parts = NovelChapterPartModel::getInstance()->getWhere(['planid'=>$data['planid'],'batch'=>$batch,'isdel'=>0])
            ->orderBy('number','asc')->get();

        $requestIdMap = [];
        foreach ($parts as $val){
            $prompt = $val['prompt'];
            //todo 2 根据描述词生成ai漫画图片
            if(!empty($val['roleids'])){
                $roleids = json_decode($val['roleids'],true);
                $roles = [];
                is_array($roleids) && $roles = NovelPlanRoleModel::getInstance()->getWhere([['id','in',$roleids],'isdel'=>0])->get()->toArray();
                if($roles){
                    $fixPrompt = '';
                    $endPrompt = '';
                    foreach($roles as $role){
                        $fixPrompt .=  $role['sex']==2?'1 gril,' : '1 boy,';
                        $fixPrompt .=  $role['prompt'].',';

                        $endPrompt .= ',<lora:'.$role['rola'].':1>';
                    }

                    $prompt = ($fixPrompt.$prompt.$endPrompt);
                }
            }

            $options = ['batch_size'=>'2','seed'=>($val['ai_image_command_seed']?:-1),'command_template_id'=>$plan['ai_image_command_id']??0];
            if(!empty($val['more_people_prompt'])){
                $options['more_people_prompt'] = $val['more_people_prompt'];
            }
            $requestId = Common::generatedAIImageByTemplate($plan['ai_image_command'],$prompt,$modelVal,$options,'admin_plan_part_view_image_'.$val['id'],$data['requestid']??'');
            $requestIdMap[$requestId] = $val;
        }

        foreach ($requestIdMap as $requestId=>$val){
            try{
                while (true){
                    $images = Common::getAiImageResult($requestId);
                    if(!empty($images) && is_array($images)){
                        $update = [];
                        foreach($images as $k=>$image){
                            $k==0 && $update['ai_images1'] =$image;
                            $k==1 && $update['ai_images2'] =$image;
                            $k==2 && $update['ai_images3'] =$image;
                            $k==3 && $update['ai_images4'] =$image;
                        }
                        NovelChapterPartModel::getInstance()->getWhere(['id'=>$val['id']])->update($update);
                        break;
                    }
                    sleep(3);

                    $first = (new AiImageRequestQueueModel())->getWhere(['requestid'=>$requestId])->first();
                    if($first && !empty($first['start_time']) && (time()-strtotime($first['start_time']))>1200){
                        break;
                    }elseif ($first==false){
                        break;
                    }
                }
            }catch (IndexException $e){
                //异常不做处理
            }
        }

        return true;
    }

    // 消费失败回调
    /*
    $package = [
        'id' => 1357277951, // 消息ID
        'time' => 1709170510, // 消息时间
        'delay' => 0, // 延迟时间
        'attempts' => 2, // 消费次数
        'queue' => 'send-mail', // 队列名
        'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
        'max_attempts' => 5, // 最大重试次数
        'error' => '错误信息' // 错误信息
    ]
    */
    public function onConsumeFailure(\Throwable $e, $package)
    {
//        echo "consume failure\n";
//        echo $e->getMessage() . "\n";
//        // 无需反序列化
//        var_export($package);

        RedisQueue::getInstance()->saveQueue($this->queue,$package['data'],['error'=>$e->getMessage()]);
    }

}